package net.grapsus.arduino;

import java.util.*;
import java.io.*;

import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.TokenStream;
import org.antlr.runtime.tree.CommonTree;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.*;

public class ArduinoCompiler implements Opcodes
{
	/* antlr stuff */
	private ArduinoLexer lexer;
	private ArduinoParser parser;
	private CharStream stream;
	private CommonTree ast;
	private String[] tokenNames;
	
	/* compilation stuff */
	private String className; /* output class name */
	private HashMap<String, Variable> globalVariables;
	private HashMap<String, Variable> localVariables;
	private int localVariablesNextNum;
	private HashMap<String, Function> localFunctions;

	/* api stuff */
	private String apiFile;
	private Type apiType;
	private HashMap<String, Function> apiFunctions;
	private HashMap<String, Variable> apiVariables;
	private HashMap<String, Variable> apiStaticVariables;
	
	public ArduinoCompiler()
	{
		apiType = Type.getObjectType("net/grapsus/arduino/ArduinoCore");
	}

	/* Parse this code */
	public boolean parseString(String code)
	{
		stream = new ANTLRStringStream(code);
		return parse();
	}

	/* Parse this file */
	public boolean parseFile(String filename)
	{
		try
		{
			stream = new ANTLRFileStream(filename);
		}
		catch(IOException e)
		{
			return false;
		}

		return parse();
	}

	/* run ANTLR parser and lexer on previously defined input stream */
	private boolean parse()
	{
		try
		{
			lexer = new ArduinoLexer(stream);
			CommonTokenStream tokenStream = new CommonTokenStream(lexer);
			parser = new ArduinoParser(tokenStream);
			ArduinoParser.program_return program = parser.program();
			ast = (CommonTree)program.getTree();
			tokenNames = parser.getTokenNames();
		}
		catch(RecognitionException e)
		{
			return false;
		}
		//System.out.println(ast.toStringTree());
		return true;
	}
	
	/* parse Arduino API class */
	public void parseApi() throws IOException
	{
   	ClassReader crApi;
		crApi = new ClassReader(apiType.getClassName());

		ApiParser p = new ApiParser();
    crApi.accept(p, 0);

		apiFunctions = p.getFunctions();
		apiVariables = p.getVariables();
		apiStaticVariables = p.getStaticVariables();
	}

	/* Compile this program into a Java class */
	public void classGen(String className, String outputFile) throws IOException, Exception
	{
		FileOutputStream fos = new FileOutputStream(outputFile);
		fos.write(this.classGen(className));
		fos.close();
	}

	public byte[] classGen(String className) throws Exception
	{
		this.className = className;
		
		HashMap<String,CommonTree> constructorGlobalInit = new HashMap<String,CommonTree>();

		/* parse API */
		parseApi();	
		
		/* init global variables */
		globalVariables = new HashMap<String,Variable>();

		/* init asm */
		ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
		FieldVisitor fv;
		MethodVisitor mv;

		/* write class header */
		cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, className, null, apiType.getInternalName(), null);
		
		/* Compile AST top level */
		for(int i = 0; i < this.ast.getChildCount(); i++)
		{
			CommonTree child = (CommonTree)this.ast.getChild(i);

			if(child.getText().equals("DIM") || child.getText().equals("="))
			{
				if(child.getText().equals("="))
				{
					constructorGlobalInit.put(child.getChild(0).getChild(1).getText(), (CommonTree)child.getChild(1));
					child = (CommonTree)child.getChild(0);
				}
				String desc = ctype2jvm(child);
				String name = child.getChild(1).getText();
				Type type = Type.getType(desc);

				System.out.println("GLOBAL DIM " + name + " " + desc);
				cw.visitField(ACC_PUBLIC, name, type.getDescriptor(), null, null);
				globalVariables.put(name, new Variable(desc));
			}
			else if(child.getText().equals("FUNC_DECL"))
			{
				System.out.println("FUNC_DECL " + child.getChild(0).getChild(1).getText());
				/* TODO: fix void params and return */
				mv = cw.visitMethod(ACC_PROTECTED, child.getChild(0).getChild(1).getText(), "()V", null, null);
				mv.visitCode();
				compileMethod(child, mv);
				mv.visitInsn(RETURN); /* FIXME */
				mv.visitMaxs(0, 0);
		    mv.visitEnd();
			}
			else
			{
				throw new Exception("ERROR: unknown top-level node " + child.getText());
			}
		}
		
		/* write constructor */
		mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
		mv.visitCode();
		/* init this object */
		mv.visitVarInsn(ALOAD, 0);
		mv.visitMethodInsn(INVOKESPECIAL, apiType.getInternalName(), "<init>", "()V");

		/* init global variables */
		Iterator it = constructorGlobalInit.keySet().iterator();
		InstructionAdapter ia = new InstructionAdapter(mv);
		while(it.hasNext())
		{
			String key = (String)it.next();
			CommonTree val = constructorGlobalInit.get(key);
		 	
  		mv.visitVarInsn(ALOAD, 0); 
			Type varType = globalVariables.get(key).getType();
      Type valType = compileExpr(val, mv);
      if( ! varType.equals(valType))
      {
        System.out.println("WRNING: global initialization requires cast");
        ia.cast(valType, varType);
      }
     	
			mv.visitFieldInsn(PUTFIELD, className, key, varType.getDescriptor());
		}
		
		mv.visitInsn(RETURN);
		mv.visitMaxs(0, 0);
		mv.visitEnd();

		/* finish class and write to file */
		cw.visitEnd();
		return cw.toByteArray();
	}
	
	/* compile AST into a MethodVisitor */
	private void compileMethod(CommonTree ast, MethodVisitor mv) throws Exception
	{
		/* TODO: check type etc. */
		/* FIXME: register method to local methods hash */
		localVariables = new HashMap<String,Variable>();
		localVariablesNextNum = 1;
		compileBlock((CommonTree)(ast.getChild(2)), mv);
	}
	
	/* produce bytecode for the block in ast into mv */
	private void compileBlock(CommonTree ast, MethodVisitor mv) throws Exception
	{
		/* compile block statements */
		for(int i = 0; i < ast.getChildCount(); i++)
		{
			CommonTree child = (CommonTree)ast.getChild(i);

			if(child.getText() != null) /* empty statement */ 
			{
				compileStatement(child, mv);
			}
		}
	}
	
	/* produce bytecode for the statement in ast into mv */
	private void compileStatement(CommonTree ast, MethodVisitor mv) throws Exception
	{
		InstructionAdapter ia = new InstructionAdapter(mv);
		/* special statements */
		if(ast.getText().equals("WHILE"))
		{
			System.out.println("WHILE found");
			Label l0 = new Label();
			mv.visitLabel(l0);
			mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
			Type t = compileExpr((CommonTree)ast.getChild(0), mv);
			if(t.equals(Type.VOID_TYPE))
			{
				throw new Exception("ERROR: while loop requires a non void condition");
			}
			else if( ! t.equals(Type.INT_TYPE))
			{
				compileLogicCast(t, mv);
			}
			Label l1 = new Label();
			ia.ifeq(l1);
			compileBlock((CommonTree)ast.getChild(1), mv); /* FIXME: check if we have block or statement */
			ia.goTo(l0);
			mv.visitLabel(l1);
			mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
		}
		else if(ast.getText().equals("FOR"))
		{
			System.out.println("FOR found");

			/* place initialization statement */
			if(ast.getChild(0).getChildCount() > 0) /* FOR_INIT */
			{
				compileStatement((CommonTree)ast.getChild(0).getChild(0), mv);
			}
			
			/* place condition */
			Label l0 = new Label();
			mv.visitLabel(l0);
			mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
			Type t;
			if(ast.getChild(1).getChildCount() > 0) /* FOR_COND */
			{
				t = compileExpr((CommonTree)ast.getChild(1).getChild(0), mv);
			}
			else
			{
				t = Type.VOID_TYPE;
			}

			if(t.equals(Type.VOID_TYPE)) /* FOR_COND void means true */
			{
				tconst(Type.INT_TYPE, 1, ia);
				t = Type.INT_TYPE;
			}
			if( ! t.equals(Type.INT_TYPE))
			{
				compileLogicCast(t, mv);
			}

			Label l1 = new Label();
			ia.ifeq(l1);
			compileBlock((CommonTree)ast.getChild(3), mv); /* FIXME: check if we have block or statement */
			if(ast.getChild(2).getChildCount() > 0) /* FOR_INC */
	    {
	    	compileStatement((CommonTree)ast.getChild(2).getChild(0), mv);
			}
			ia.goTo(l0);
			mv.visitLabel(l1);
			mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
		}
		else if(ast.getText().equals("IF"))
		{
			System.out.println("IF found");
			Type t = compileExpr((CommonTree)ast.getChild(0), mv);
			if(t.equals(Type.VOID_TYPE))
			{
				throw new Exception("ERROR: if statement requires a non void condition");
			}
			else if( ! t.equals(Type.INT_TYPE))
			{
				compileLogicCast(t, mv);
			}

			Label lskip = new Label();
			Label lend = new Label();
			ia.ifeq(lskip);
			compileBlock((CommonTree)ast.getChild(1), mv); /* FIXME: check if we have block or statement */
			ia.goTo(lend);
			mv.visitLabel(lskip);
			mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
			if(ast.getChildCount() > 2) /* we have else statement */
			{
				compileBlock((CommonTree)ast.getChild(2), mv); /* FIXME: check if we have block or statement */
			}
			mv.visitLabel(lend);
			mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
		}
		else
		{
			/* all other statements are expr in C++ */
			Type t = compileExpr(ast, mv);
			/* throw result remaining on the stack */
			if(t.getSize() == 1)
			{
				mv.visitInsn(POP);
			}
			else if(t.getSize() == 2)
			{
				mv.visitInsn(POP2);
			}
		}
	}
	
	private Type compileExpr(CommonTree ast, MethodVisitor mv) throws Exception
	{
		InstructionAdapter ia = new InstructionAdapter(mv);		
		String type = tokenNames[ast.getType()];
		String text = ast.getText();
		
		/*System.out.println("# " + text + " <" + type + ">");*/

		if(text.equals("BLOCK"))
		{
			compileBlock(ast, mv);
			return Type.VOID_TYPE;
		}
		else if(text.equals("WHILE") || text.equals("FOR") || text.equals("IF"))
		{
			compileStatement(ast, mv);
			return Type.VOID_TYPE;
		}
		else if(type.equals("INT"))
		{
			System.out.println("INT " + text);
			ia.iconst(new Integer(text));
			return Type.INT_TYPE;
		}
		else if(type.equals("FLOAT"))
		{
			System.out.println("FLOAT " + text);
			ia.dconst(new Double(text));
			return Type.DOUBLE_TYPE;
		}
		else if(type.equals("ID"))
		{
			System.out.println("ID " + text);
			if(apiVariables.containsKey(text))
			{
				System.out.println("ID found in API");
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, className, text, apiVariables.get(text).getType().getDescriptor());
				return apiVariables.get(text).getType();
			}
			else if(apiStaticVariables.containsKey(text))
			{
				System.out.println("ID found in static API");
				mv.visitFieldInsn(GETSTATIC, apiType.getInternalName(), text, apiStaticVariables.get(text).getType().getDescriptor());
				return apiStaticVariables.get(text).getType();
			}
			else if(globalVariables.containsKey(text))
			{
				System.out.println("ID found in global variables");
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, className, text, globalVariables.get(text).getType().getDescriptor());
				return globalVariables.get(text).getType();
			}
			else if(localVariables.containsKey(text))
			{
				System.out.println("ID found in local variables");
				Variable var = localVariables.get(text);
				ia.load(var.getNum(), var.getType());
				return var.getType();
			}
			else
			{
				throw new Exception("ERROR: undefined ID " + text);	
			}
			/* FIXME: handle other types of ID (global variables) */
			//mv.visitVarInsn(ILOAD, ((Integer)compileVars.get(text)).intValue());
		}
		else if(text.equals("PRE_INC") || text.equals("PRE_DEC") || text.equals("POST_INC") || text.equals("POST_DEC"))
		{
			if(localVariables.containsKey(ast.getChild(0).getText()))
			{
				Variable var = localVariables.get(ast.getChild(0).getText());
				System.out.println("PRE/POST INC/DEC " + ast.getChild(0).getText());
				if(text.equals("PRE_INC") || text.equals("PRE_DEC"))
				{
					ia.load(var.getNum(), var.getType());
					ia.load(var.getNum(), var.getType());
					if(text.equals("PRE_INC"))
						tconst(var.getType(), 1, ia);
					else
						tconst(var.getType(), -1, ia);
					ia.add(var.getType());
					ia.store(var.getNum(), var.getType());
				}
				else
				{
					ia.load(var.getNum(), var.getType());
					if(text.equals("POST_INC"))
						tconst(var.getType(), 1, ia);
					else
						tconst(var.getType(), -1, ia);
					ia.add(var.getType());
					ia.store(var.getNum(), var.getType());
					ia.load(var.getNum(), var.getType());
				}
				return var.getType();
			}
			else
			{
				throw new Exception("FIXME : dec/incrementing non local variables not supported");
			}
		}
		else if(type.equals("FUNC_CALL"))
		{	
			return compileFuncCall((CommonTree)ast.getChild(0), mv);
		}
		else if(text.equals("="))
		{
			String id;
			if(ast.getChild(0).getText().equals("DIM")) // affectation + declaration
			{
				compileLocalDim((CommonTree)ast.getChild(0), mv);
				id = ast.getChild(0).getChild(1).getText();
			}
			else
			{
				id = ast.getChild(0).getText();
				if( ! localVariables.containsKey(id) && ! globalVariables.containsKey(id))
				{
					throw new Exception("ERROR : undefined id " + id);
				}
			}
			Variable var;
			if(localVariables.containsKey(id))
			{
				System.out.println("LET "+ id );
				var = localVariables.get(id);
			}
			else
			{
				System.out.println("GLOBAL LET "+ id );
				var = globalVariables.get(id);	
				mv.visitVarInsn(ALOAD, 0);
			}
			Type varType = var.getType();
			System.out.println("LOL " + ast.getChild(1).getText() + " " + ast.getChild(1).getChildCount());
			Type valType = compileExpr((CommonTree)ast.getChild(1), mv);
			if( ! varType.equals(valType))
			{
				System.out.println("WRNING: assignement requires cast");
				ia.cast(valType, varType);
			}
			if(localVariables.containsKey(id))
			{
				ia.store(var.getNum(), varType);
			}
			else if(globalVariables.containsKey(id))
			{
				mv.visitFieldInsn(PUTFIELD, className, id, var.getType().getDescriptor());
			}

			/* FIXME: handle DUP or DUP2 and return the right type */
			return Type.VOID_TYPE;
		}
		else if(ast.getChildCount() == 2) // binary OP
		{
			/* arithmetic bitwise and comparison operations */
			if(operatorIsArithmetic(text) || operatorIsBitwise(text) || operatorIsComp(text))
			{
				/* eval left side */
				Type tleft = compileExpr((CommonTree)ast.getChild(0), mv);	
				/* eval right side */
				Type tright = compileExpr((CommonTree)ast.getChild(1), mv);	
				/* match right side type to the left side if necessary */
				if( ! tleft.equals(tright))
				{
					System.out.println("WARNING: binary operation requires implicit cast");
					ia.cast(tright, tleft);
				}
				
				/* place operation */
				if(operatorIsArithmetic(text))
				{
					compileBinaryArithmeticOp(text, tleft, mv);
					return tleft;
				}
				else if(operatorIsBitwise(text))
				{
					compileBinaryBitOp(text, tleft, mv);
					return tleft;
				}
				else if(operatorIsComp(text))
				{
					compileBinaryCompOp(text, tleft, mv);	
					return Type.INT_TYPE;
				}
			}
			else if(operatorIsLogic(text)) 
			{
				/* eval left side and convert it to logic */
				Type tleft = compileExpr((CommonTree)ast.getChild(0), mv);
				if( ! tleft.equals(Type.INT_TYPE))
					compileLogicCast(tleft, mv);
				/* eval right side and convert it to logic */
        Type tright = compileExpr((CommonTree)ast.getChild(1), mv);
				if( ! tright.equals(Type.INT_TYPE))
					compileLogicCast(tright, mv);
				/* place operation */
				compileBinaryLogicOp(text, mv);
				return Type.INT_TYPE;
			}
			else
			{
				throw new Exception("ERROR: unknown binary operator "+text);
			}
		}
		else if(ast.getChildCount() == 1) // unary OP
		{
			System.out.println("negation");
			if(text.equals("-"))
			{
				Type t = compileExpr((CommonTree)ast.getChild(0), mv);	
				ia.neg(t);
				System.out.println("negation");
				return t;
			}
			else if(text.equals("!"))
			{
				Type t = compileExpr((CommonTree)ast.getChild(0), mv);	
				/* !x => 1 - logiccast(x); */
				compileLogicCast(t, mv);
				ia.iconst(1);
				ia.sub(Type.INT_TYPE);
				ia.neg(Type.INT_TYPE);
				return Type.INT_TYPE;
			}
			else if(text.equals("~"))
			{
				Type t = compileExpr((CommonTree)ast.getChild(0), mv);	
				/* ~x => x^-1 !!! */
				tconst(t, -1, ia);
				ia.xor(t);
				return t;
			}
		}
		return Type.INT_TYPE;
	}
	
	/* produces all the necessary bytecode for function call in ast to mv */
	private Type compileFuncCall(CommonTree ast, MethodVisitor mv) throws Exception
	{
		InstructionAdapter ia = new InstructionAdapter(mv);
		
		/* FIXME: handle calls to non API functions */
		
		String name = ast.getText();
		System.out.println("FUNC_CALL " + name);

		/* check name */
		if( ! apiFunctions.containsKey(name))
		{
			throw new Exception("ERROR : no such function !");
		}
		
		Type[] args = apiFunctions.get(name).getArguments();
		/* check argument count */
		if(ast.getChildCount() != args.length)
		{
			throw new Exception("ERROR : argument count mismatch (excpected + "
			 + ast.getChildCount() + ", "
			 + args.length+" given)");
		}

		/* stack this ref */
		mv.visitVarInsn(ALOAD, 0);
		
		/* stack arguments */
		for(int i = 0; i < ast.getChildCount(); i++)
		{
			CommonTree node = (CommonTree)ast.getChild(i);
			Type exprType = compileExpr(node, mv);
			if( ! exprType.equals(args[i]))
			{
				System.out.println("WARNING : arg " + i + " requires cast");
				ia.cast(exprType, args[i]);
			}
		}
		/* compile call */
		mv.visitMethodInsn(
			INVOKEVIRTUAL,
			className,
			name,
			apiFunctions.get(name).getDescriptor()
		);
		
		return apiFunctions.get(name).getReturn();
	}
	
	private void compileBinaryArithmeticOp(String op, Type t, MethodVisitor mv)
	{
		InstructionAdapter ia = new InstructionAdapter(mv);

		if(op.equals("+"))
			ia.add(t);
		else if(op.equals("-"))
			ia.sub(t);
		else if(op.equals("*"))
			ia.mul(t);
		else if(op.equals("/"))
			ia.div(t);
		else if(op.equals("%"))
			ia.rem(t);
	}
	
	private void compileBinaryBitOp(String op, Type t, MethodVisitor mv)
	{
		InstructionAdapter ia = new InstructionAdapter(mv);

		if(op.equals("&"))
			ia.and(t);
		else if(op.equals("|"))
			ia.or(t);
		else if(op.equals("^"))
			ia.xor(t);
		else if(op.equals(">>"))
			ia.ushr(t);
		else if(op.equals("<<"))
			ia.shl(t);
	}
	
	private void compileBinaryCompOp(String op, Type t, MethodVisitor mv) throws Exception
	{
		InstructionAdapter ia = new InstructionAdapter(mv);
		
		/* now this is tricky to handle comparisons in JVM
			 we'll try to emulate C behaviour : 0 is false
			 all other value is true */

		if(t.equals(Type.INT_TYPE) || t.equals(Type.SHORT_TYPE) || t.equals(Type.BYTE_TYPE))
		{
			mv.visitInsn(ISUB);
		}
		else if(t.equals(Type.LONG_TYPE))
		{
			mv.visitInsn(LCMP);
		}
		else if(t.equals(Type.FLOAT_TYPE))
		{
 			mv.visitInsn(FCMPL);
		}
		else if(t.equals(Type.DOUBLE_TYPE))
		{
 			mv.visitInsn(DCMPL);
		}
		else
		{
			throw new Exception("ERROR: cannot compare type " + t.toString());
		}
		
		Label lOne = new Label();
		Label lEnd = new Label();
		/* now for a < b we have something like a - b converted to int */
		if(op.equals("<"))
			ia.iflt(lOne);
		else if(op.equals(">"))
			ia.ifgt(lOne);
		else if(op.equals("<="))
			ia.ifle(lOne);
		else if(op.equals(">="))
			ia.ifge(lOne);
		else if(op.equals("=="))
			ia.ifeq(lOne);
		else if(op.equals("!="))
			ia.ifne(lOne);
		
		/* push 0 */
		ia.iconst(0);
		ia.goTo(lEnd);
		mv.visitLabel(lOne);
		mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
		/* push 1 */
		ia.iconst(1);
		mv.visitLabel(lEnd);
		mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
	}
	
	private Type compileBinaryLogicOp(String op, MethodVisitor mv)
	{
		if(op.equals("||"))
		      mv.visitInsn(IOR);
    else if(op.equals("&&"))
		      mv.visitInsn(IAND);
		
		return Type.INT_TYPE;
	}
	
	private void compileLogicCast(Type t, MethodVisitor mv) throws Exception
	{
		InstructionAdapter ia = new InstructionAdapter(mv);
		/* push 0 to stack */
		tconst(t, 0, ia);
		/* compare */
		compileBinaryCompOp("==", t, mv);
	}
	
	private Type compileLocalDim(CommonTree ast, MethodVisitor mv) throws Exception
	{
		String desc = ctype2jvm(ast);
		String name = ast.getChild(1).getText();
		Type type = Type.getType(desc);
		
		System.out.println("DIM " + name + " " + desc);
		localVariables.put(name, new Variable(desc, localVariablesNextNum));
		localVariablesNextNum += type.getSize();
		/* FIXME: handle desclaration scope */
		return type;
	}
	
	/* convert C declaration to jvm type */
	private String ctype2jvm(CommonTree ast) throws Exception
	{
		HashMap<String,String> c2j = new HashMap<String,String>();
		c2j.put("char", "B");c2j.put("uint8_t", "B");c2j.put("int8_t", "B");
		c2j.put("short", "S");c2j.put("uint16_t", "S");c2j.put("int16_t", "S");
		c2j.put("int", "I");c2j.put("uint32_t", "I");c2j.put("int32_t", "I");
		c2j.put("long int", "J");c2j.put("uint64_t", "J");c2j.put("int64_t", "J");
		c2j.put("double", "D");
		c2j.put("float", "F");

		CommonTree prefix = (CommonTree)ast.getChild(0).getChild(0);
		if( ! c2j.containsKey(prefix.getText()))
		{
			throw new Exception("ERROR : tpye " + prefix.getText() + " is not available, sorry");
		}
		String base = c2j.get(prefix.getText());
		
		/* FIXME: handle array dimensions */
		return base;
	}
	
	private boolean operatorIsArithmetic(String op)
	{
		return op.equals("+") || op.equals("-") || op.equals("*") || op.equals("/") || op.equals("%") ;
	}
			
	private boolean operatorIsBitwise(String op)
	{
		return op.equals("&") || op.equals("|") || op.equals("^") || op.equals(">>") || op.equals("<<");
	}
	
	private boolean operatorIsComp(String op)
	{
		return op.equals("<") || op.equals(">") || op.equals("<=") || op.equals(">=") || op.equals("==") || op.equals("!=");
	}
	
	private boolean operatorIsLogic(String op)
	{
		return op.equals("&&") || op.equals("||");
	}

	void tconst(Type t, int value, InstructionAdapter ia) throws Exception
	{
		if(t.equals(Type.INT_TYPE) || t.equals(Type.SHORT_TYPE) || t.equals(Type.BYTE_TYPE))
			ia.iconst(value);
		else if(t.equals(Type.FLOAT_TYPE))
			ia.fconst(value);
		else if(t.equals(Type.DOUBLE_TYPE))
			ia.dconst(value);
		else
		{
			throw new Exception("ERROR: insupported for type for tconst() " + t.toString());
		}
	}
}
